package com.happy.unitconvert.controller;

import com.happy.unitconvert.common.result.Result;
import com.happy.unitconvert.model.dto.ExchangeRateDataWrapper;
import com.happy.unitconvert.service.ExchangeRateService;
import com.happy.unitconvert.service.ExchangeRateServiceExtended;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;

import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

/**
 * 汇率控制器
 * 提供汇率查询和货币转换的REST API
 *
 * @author Happy
 */
@Slf4j
@RestController
@RequestMapping("/api/v1/exchange-rate")
@RequiredArgsConstructor
@Validated
@Tag(name = "汇率管理", description = "汇率查询和货币转换接口")
public class ExchangeRateController {

  private final ExchangeRateService exchangeRateService;
  private final ExchangeRateServiceExtended exchangeRateServiceExtended;

  @GetMapping("/rates/{baseCurrency}")
  @Operation(summary = "获取汇率列表（包含数据来源）", description = "获取指定基础货币的所有汇率数据，包含数据来源信息")
  public Mono<Result<ExchangeRateDataWrapper<Map<String, BigDecimal>>>> getExchangeRatesWithSource(
      @Parameter(description = "基础货币代码", example = "USD") @PathVariable @NotBlank String baseCurrency,
      @Parameter(description = "是否强制从网络更新数据", example = "false") @RequestParam(required = false, defaultValue = "false") boolean isForceUpdate) {

    log.info("接收获取汇率请求（包含来源信息），基础货币: {}，强制更新: {}", baseCurrency, isForceUpdate);

    return exchangeRateServiceExtended.getExchangeRatesWithSource(baseCurrency, isForceUpdate)
        .map(Result::success)
        .doOnNext(result -> {
          ExchangeRateDataWrapper<Map<String, BigDecimal>> wrapper = result.getData();
          log.info("返回汇率数据，基础货币: {}，包含{}种货币汇率，数据来源: {}，强制更新: {}",
              baseCurrency, wrapper.getList().size(), wrapper.isFromCache() ? "缓存" : "API", isForceUpdate);
        })
        .onErrorResume(error -> {
          log.error("获取汇率失败，基础货币: {}，错误: {}", baseCurrency, error.getMessage());
          return Mono.just(Result.error("获取汇率失败: " + error.getMessage()));
        });
  }

  @GetMapping("/rate")
  @Operation(summary = "获取指定货币对汇率", description = "获取从源货币到目标货币的汇率")
  public Mono<Result<BigDecimal>> getExchangeRate(
      @Parameter(description = "源货币代码", example = "USD") @RequestParam @NotBlank String from,
      @Parameter(description = "目标货币代码", example = "CNY") @RequestParam @NotBlank String to) {

    log.info("接收获取特定汇率请求，从{}到{}", from, to);

    return exchangeRateService.getExchangeRate(from, to)
        .map(Result::success)
        .doOnNext(result -> log.info("返回汇率，从{}到{}，汇率: {}", from, to, result.getData()))
        .onErrorResume(error -> {
          log.error("获取汇率失败，从{}到{}，错误: {}", from, to, error.getMessage());
          return Mono.just(Result.error("获取汇率失败: " + error.getMessage()));
        });
  }

  @PostMapping("/convert")
  @Operation(summary = "货币转换", description = "将指定金额从源货币转换为目标货币")
  public Mono<Result<BigDecimal>> convertCurrency(
      @Parameter(description = "转换金额", example = "100.00") @RequestParam @DecimalMin(value = "0.01", message = "金额必须大于0") BigDecimal amount,
      @Parameter(description = "源货币代码", example = "USD") @RequestParam @NotBlank String from,
      @Parameter(description = "目标货币代码", example = "CNY") @RequestParam @NotBlank String to) {

    log.info("接收货币转换请求，金额: {}，从{}到{}", amount, from, to);

    return exchangeRateService.convertCurrency(amount, from, to)
        .map(Result::success)
        .doOnNext(result -> log.info("货币转换完成，原金额: {} {}，转换后: {} {}",
            amount, from, result.getData(), to))
        .onErrorResume(error -> {
          log.error("货币转换失败，金额: {}，从{}到{}，错误: {}", amount, from, to, error.getMessage());
          return Mono.just(Result.error("货币转换失败: " + error.getMessage()));
        });
  }

  @PostMapping("/convert/batch")
  @Operation(summary = "批量货币转换", description = "将指定金额从源货币转换为多种目标货币")
  public Mono<Result<Map<String, BigDecimal>>> convertCurrencyBatch(
      @Parameter(description = "转换金额", example = "100.00") @RequestParam @DecimalMin(value = "0.01", message = "金额必须大于0") BigDecimal amount,
      @Parameter(description = "源货币代码", example = "USD") @RequestParam @NotBlank String from,
      @Parameter(description = "目标货币代码列表", example = "[\"CNY\", \"EUR\", \"GBP\"]") @RequestBody @NotEmpty List<String> toCurrencies) {

    log.info("接收批量货币转换请求，金额: {}，从{}到{}种货币", amount, from, toCurrencies.size());

    return exchangeRateService.convertCurrencyBatch(amount, from, toCurrencies)
        .map(Result::success)
        .doOnNext(result -> log.info("批量货币转换完成，转换了{}种货币", result.getData().size()))
        .onErrorResume(error -> {
          log.error("批量货币转换失败，金额: {}，从{}，错误: {}", amount, from, error.getMessage());
          return Mono.just(Result.error("批量货币转换失败: " + error.getMessage()));
        });
  }

  @GetMapping("/currencies")
  @Operation(summary = "获取支持的货币列表", description = "获取系统支持的所有货币代码")
  public Mono<Result<List<String>>> getSupportedCurrencies() {

    log.info("接收获取支持货币列表请求");

    return exchangeRateService.getSupportedCurrencies()
        .map(Result::success)
        .doOnNext(result -> log.info("返回支持的货币列表，共{}种货币", result.getData().size()))
        .onErrorResume(error -> {
          log.error("获取支持货币列表失败，错误: {}", error.getMessage());
          return Mono.just(Result.error("获取支持货币列表失败: " + error.getMessage()));
        });
  }

  @PostMapping("/cache/refresh")
  @Operation(summary = "刷新汇率缓存", description = "刷新指定货币或所有货币的汇率缓存")
  public Mono<Result<String>> refreshCache(
      @Parameter(description = "基础货币代码，为空则刷新所有缓存") @RequestParam(required = false) String baseCurrency) {

    log.info("接收刷新缓存请求，基础货币: {}", baseCurrency);

    return exchangeRateService.refreshCache(baseCurrency)
        .then(Mono.just(Result.success("缓存刷新成功")))
        .doOnNext(result -> log.info("缓存刷新完成，基础货币: {}", baseCurrency))
        .onErrorResume(error -> {
          log.error("刷新缓存失败，基础货币: {}，错误: {}", baseCurrency, error.getMessage());
          return Mono.just(Result.error("刷新缓存失败: " + error.getMessage()));
        });
  }

  @GetMapping("/health")
  @Operation(summary = "检查服务健康状态", description = "检查汇率服务是否正常工作")
  public Mono<Result<Map<String, Object>>> checkHealth() {

    log.info("接收健康检查请求");

    return exchangeRateService.isServiceAvailable()
        .map(available -> {
          Map<String, Object> health = Map.of(
              "status", available ? "UP" : "DOWN",
              "service", "ExchangeRateService",
              "timestamp", System.currentTimeMillis());
          return Result.success(health);
        })
        .doOnNext(result -> {
          Map<String, Object> health = (Map<String, Object>) result.getData();
          log.info("服务健康检查完成，状态: {}", health.get("status"));
        })
        .onErrorResume(error -> {
          log.error("健康检查失败，错误: {}", error.getMessage());
          Map<String, Object> health = Map.of(
              "status", "DOWN",
              "service", "ExchangeRateService",
              "error", error.getMessage(),
              "timestamp", System.currentTimeMillis());
          return Mono.just(Result.success(health));
        });
  }

}